﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;

namespace Brainee.Search
{
  /// <summary>
  /// A single, general TREE-SEARCH algorithm that can be used to solve any problem;
  /// specific variants of the algorithm embody different strategies.
  /// </summary>
  public class TreeSearch : ISearchAlgorithm
  {
#region Properties

    /// <summary>
    /// The strategy to use with this search tree
    /// </summary>
    /// <remarks>This property must be set before calling SearchTree.Solve()</remarks>
    public ISearchStrategy Strategy;

#endregion
#region Events

    /// <summary>
    /// An event that is raised whenever a solution is found.
    /// </summary>
    public event EventHandler<SolutionFoundEventArg> SolutionFound;

#endregion
#region ISearchAlgorithm implementation

    /// <summary>
    /// Solve the problem with current strategy.
    /// </summary>
    /// <param name="problem">The problem to solve</param>
    /// <returns>The solution of the problem if it exists. Otherwise, PathSolution.None.</returns>
    public PathSolution Solve (IProblem problem)
    {
      if (problem == null)
        throw new ArgumentNullException ("problem");
      if (Strategy == null)
        throw new InvalidOperationException ("A strategy must be defined");

      // Initialize the strategy with the problem
      Strategy.Initialize (problem);

      ISearchState node = null;
      while ((node = Strategy.NextState ()) != null)
      {
        // If the current node is not the goal, then 
        // expand its childs and continue the loop
        if (!problem.GoalTest (node.State))
        {
          Strategy.Expand (node, problem);
          continue;
        }

        // A solution has been found
        PathSolution sol = new PathSolution (node);

        // If no handler has been registered, return this solution
        if (SolutionFound == null)
          return sol;

        // Raise the SolutionFound event and check if we should stop here.
        SolutionFoundEventArg e = new SolutionFoundEventArg (sol);
        SolutionFound (this, e);
        if (e.Continue == false)
          return sol;
      }

      // No solution has been found
      return PathSolution.None;
    }

#endregion
  }

  public class SolutionFoundEventArg : EventArgs
  {
    public PathSolution Solution { get; private set; }
    public bool Continue { get; set; }

    public SolutionFoundEventArg (PathSolution s)
    {
      Solution = s;
      Continue = false;
    }
  }
}
